#include <stdio.h>
#include "pico/stdlib.h"
#include "pico/binary_info.h"
#include "hardware/spi.h"

#include "wizchip_conf.h"
#include "bsp_spi.h"
#include "dhcp.h"       // Use dhcp
#include "socket.h"     // Use socket
#include "httpServer.h" // Use http
#include "webpage.h"    //Web Source Code
#include "websocket.h"

#define SOCK_DHCP 0                      // Socket number
#define SOCK_WEBSOCKET 1
#define ETHERNET_BUF_MAX_SIZE (1024 * 2) // Send and receive cache size
#define DHCP_RETRY_COUNT 5               // DHCP retry times
#define MAX_HTTPSOCK 2                   // MAX HTTP SOCKET

/**
 * @brief   Timer callback processing function, used for dhcp timing processing
 * @param   repeating :Timer structure
 * @return  bool
 */
bool repeating_timer_1s_callback(struct repeating_timer *t);

/**
 * @brief   1ms Timer callback processing function
 * @param   repeating :Timer structure
 * @return  bool
 */
bool repeating_timer_1ms_callback(struct repeating_timer *t);

/**
 * @brief   Initialization of chip network information
 * @param   conf_info :Static configuration information
 * @return  none
 */
void network_init(wiz_NetInfo *conf_info);

/**
 * @brief   Serial port 0 initialization function
 * @param   none
 * @return  none
 */
void UART0_Init();

/**
 * @brief   Serial port 0 receives the callback function
 * @param   none
 * @return  none
 */
void on_uart0_rx();

/* Network information to be configured. */
wiz_NetInfo net_info = {
    .mac = {0x00, 0x44, 0x55, 0x66, 0xed, 0x2e}, // Configured MAC address
    .ip = {192, 168, 1, 10},                     // Configured IP address
    .sn = {255, 255, 255, 0},                    // Configured subnet mask
    .gw = {192, 168, 1, 1},                      // Configured gateway
    .dns = {8, 8, 8, 8},                         // Configured domain address
    .dhcp = NETINFO_DHCP};                       // Configured dhcp model,NETINFO_DHCP:use dhcp; NETINFO_STATIC: use static ip.

static uint8_t ethernet_buf[ETHERNET_BUF_MAX_SIZE] = {
    0,
};
static uint8_t http_tx_ethernet_buf[ETHERNET_BUF_MAX_SIZE] = {
    0,
};
static uint8_t http_rx_ethernet_buf[ETHERNET_BUF_MAX_SIZE] = {
    0,
};
uint32_t uart0_idle_count = 0;
static uint8_t socknumlist[] = {2, 3}; // http socknumlist
static uint8_t breakout_flag = 0;      // Define the DHCP acquisition flag

extern uint8_t rxbuf[1024]; // uart0 rx buffer
extern uint16_t rxlen;      // uart0 rx data len

int main()
{
    int i;
    struct repeating_timer timer_1s; // Define the timer structure
    struct repeating_timer timer_1ms; // Define the timer structure
    wiz_NetInfo get_info;
    /* MCU init */
    stdio_init_all();              // Initialize the main control peripheral
    wizchip_initialize();          // Initialize the chip interface
    wizchip_setnetinfo(&net_info); // Configure once first
    UART0_Init();                  // uart0 init

    /*dhcp init*/
    DHCP_init(SOCK_DHCP, ethernet_buf);                                   // DHCP initialization
    add_repeating_timer_ms(1000, repeating_timer_1s_callback, NULL, &timer_1s); // Add DHCP 1s Tick Timer handler
    add_repeating_timer_ms(1, repeating_timer_1ms_callback, NULL, &timer_1ms); // Add DHCP 1ms Tick Timer handler

    printf("wiznet chip websocket server example.\r\n");
    network_init(&net_info);                                                                // Configuring Network Information
    print_network_information(&get_info);                                                   // Read back the configuration information and print it
    httpServer_init(http_tx_ethernet_buf, http_rx_ethernet_buf, MAX_HTTPSOCK, socknumlist); // HTTP Server initialization
    reg_httpServer_webContent((uint8_t *)"index.html", (uint8_t *)INDEX_HTML);              // netinfo.html 	: Network information example page
    while (true)
    {
        for (i = 0; i < MAX_HTTPSOCK; i++)
        {
            httpServer_run(i);
        }
        do_websocket_server(SOCK_WEBSOCKET);
    }
}

void network_init(wiz_NetInfo *conf_info)
{
    int count = 0;
    uint8_t dhcp_retry = 0;

    if (conf_info->dhcp == NETINFO_DHCP)
    {
        while (true)
        {
            switch (DHCP_run()) // Do the DHCP client
            {
            case DHCP_IP_LEASED: // DHCP resolves the domain name successfully
            {
                if (breakout_flag == 0)
                {
                    printf("DHCP success\r\n");
                    getIPfromDHCP((*conf_info).ip);
                    getGWfromDHCP((*conf_info).gw);
                    getSNfromDHCP((*conf_info).sn);
                    getDNSfromDHCP((*conf_info).dns);
                    wizchip_setnetinfo(conf_info); // Configuring Network Information
                    close(SOCK_DHCP);              // After dhcp close the socket, avoid errors in later use
                    breakout_flag = 1;
                }
                break;
            }
            case DHCP_FAILED:
            {
                printf(" DHCP failed \r\n");
                count++;
                if (count <= DHCP_RETRY_COUNT) // If the number of times is less than or equal to the maximum number of times, try again
                {
                    printf("DHCP timeout occurred and retry %d \r\n", count);
                }
                else if (count > DHCP_RETRY_COUNT) // If the number of times is greater than DHCP fails
                {
                    breakout_flag = 1; // if DHCP fail, use the static
                    DHCP_stop();       // Stop processing DHCP protocol
                    conf_info->dhcp = NETINFO_STATIC;
                    wizchip_setnetinfo(conf_info); // Configuring Network Information
                    break;
                }
                break;
            }
            }
            if (breakout_flag)
            {
                printf("config success\r\n");
                break;
            }
        }
    }
    else
    {
        wizchip_setnetinfo(conf_info); // Configuring Network Information
    }
}

bool repeating_timer_1s_callback(struct repeating_timer *t)
{
    DHCP_time_handler(); // DHCP 1s Tick Timer handler
    httpServer_time_handler();
    return true;
}
bool repeating_timer_1ms_callback(struct repeating_timer *t)
{
    uart0_idle_count++;
    return true;
}

// Serial port 0 receives interrupt handlers
void on_uart0_rx(void)
{
    while (uart_is_readable(uart0))
    {
        uart0_idle_count = 0;
        uint8_t ch = uart_getc(uart0);
        rxbuf[rxlen++] = ch;
    }
}

// Serial port 0 initialization function
void UART0_Init(void)
{
    uart_init(uart0, 115200);                          // Set the baud rate of the serial port to 0
    gpio_set_function(0, GPIO_FUNC_UART);              // Set the TX pin of serial port 0 to the serial port pin
    gpio_set_function(1, GPIO_FUNC_UART);              // Set the RX pin of serial port 0 to the serial port pin
    uart_set_hw_flow(uart0, false, false);             // Example Set flow control for serial port 0
    uart_set_format(uart0, 8, 1, UART_PARITY_NONE);    // Set the serial port 0 data bit check bit stop bit
    uart_set_fifo_enabled(uart0, false);               // Whether to enable fifo
    irq_set_exclusive_handler(UART0_IRQ, on_uart0_rx); // Set the interrupt handler function for serial port 0
    irq_set_enabled(UART0_IRQ, true);                  // Enabling serial port 0 interrupted
    uart_set_irq_enables(uart0, true, false);          // Enable serial port 0 Receiving interrupted
}
